// 14.2 延迟实例化 (Lazy Instantiation)
// 延迟实例化是C++模板系统的一个关键特性，它与按需实例化密切相关，但更强调实例化发生的时机。这一机制确保模板代码只有在真正需要时才会被实例化，从而提高编译效率并支持更灵活的模板设计。

// 延迟实例化的基本概念
// 延迟实例化指的是编译器推迟模板的实例化，直到确实需要生成代码的时候才进行：
// 1.定义点不实例化：在模板定义处不会生成代码
// 2.使用点触发实例化：只有在实际使用模板时才会生成代码
// 3.实例化发生在需要完整定义的上下文：如创建对象、调用函数等

// 延迟实例化的工作原理
template <typename T>
class MyClass
{
public:
    void normalFunction()
    {
        T t;
        t.nonExistentFunction(); // 潜在错误，但在此处不会被检测
    }
};

// 此处不会实例化MyClass<int>::normalFunction
MyClass<int> obj;

void foo()
{
    // 只有在这里调用时才会实例化并检测错误
    // obj.normalFunction();  // 如果取消注释，将导致编译错误
}
// 在这个例子中：
// MyClass<int>被实例化，但normalFunction不会被实例化
// 只有当normalFunction被调用时，才会实例化并检测到错误

// 延迟实例化的优势
// 1.提高编译效率：
// -避免编译未使用的代码
// -减少编译时间和资源消耗

// 2.支持不完整类型：
template <typename T>
class Container
{
    T *elements;
    size_t count;

public:
    Container() : elements(nullptr), count(0) {}

    // 只有在调用此函数时才需要T是完整类型
    void add(const T &item)
    {
        T *newElements = new T[count + 1];
        // ...
    }
};

// 前向声明
class Incomplete;

// 可以使用不完整类型实例化
Container<Incomplete> container;
// 但不能调用需要完整类型的方法
// container.add(something);  // 错误：Incomplete是不完整类型

// 3.允许循环依赖：
template <typename T>
class Y; // 前向声明

template <typename T>
class X
{
    Y<T> *y_ptr; // 可以使用不完整类型的指针
public:
    void foo(Y<T> *y) { y_ptr = y; } // 不需要Y<T>的完整定义
};

template <typename T>
class Y
{
    X<T> x; // 使用X<T>的完整定义
};

// 延迟实例化与SFINAE
// 延迟实例化是SFINAE (Substitution Failure Is Not An Error) 机制的基础：
// 只有当T有size()方法时才有效
template <typename T>
auto get_size(const T &container) -> decltype(container.size())
{
    return container.size();
}

// 替代版本
template <typename T>
size_t get_size(...)
{
    return 0;
}

// 实际应用示例
// 延迟实例化在实际编程中有多种应用：
// 1.接口与实现分离：
// 头文件中声明模板
template <typename T>
class Interface
{
public:
    void method(); // 只声明，不定义
};

// 源文件中实现
template <typename T>
void Interface<T>::method()
{
    // 实现...
}

// 显式实例化常用类型
template class Interface<int>;

// 2.策略模式实现：
template <typename Strategy>
class Context
{
    Strategy strategy;

public:
    void execute()
    {
        strategy.execute(); // 只有在调用时才需要Strategy::execute
    }
};

// 3.编译期类型特性检测：
template <typename T, typename = void>
struct has_toString : std::false_type
{
};

template <typename T>
struct has_toString<T,
                    std::void_t<decltype(std::declval<T>().toString())>>
    : std::true_type
{
};

// 延迟实例化的限制
// 1.错误检测延迟：
//      模板错误只在实例化时检测
//      可能导致深层嵌套的错误消息
// 2.编译时间不可预测：
//      实例化可能在程序的任何地方触发
//      难以预测编译时间
// 3.调试困难：
//      错误可能出现在实例化点而非定义点
//      错误消息可能很复杂
